#pragma once

#include <vector>

#include "Code.h"
#include "Types.h"

namespace Signal
{
	class Class;
	class Function;
	class Scope;

	class Object
	{
		public:
		
		enum Type
		{
			NUMBER,
			STRING,
			INSTANCE,
			TRUE,
			FALSE,
			NIL
		};

		virtual Type type () const = 0;
	};

	class Number : public Object
	{
		public:

		Number (double_t number);

		void set (double_t number);
		double_t number () const;

		Type type () const;

		private:

		double_t m_number;
	};

	class String : public Object
	{
		public:

		String (const std::string& text);

		void set (const std::string& text);
		const std::string& text () const;

		Type type () const;

		private:

		std::string m_text;
	};

	class Instance : public Object
	{
		public:

		Instance (std::shared_ptr<Class> _class);

		std::shared_ptr<Scope> scope () const;

		Type type () const;

		private:

		std::shared_ptr<Class> m_class;
		std::shared_ptr<Scope> m_scope;
	};

	class Nil : public Object
	{
		public:

		Nil () {}

		Type type () const
		{
			return Object::NIL;
		}
	};

	class True : public Object
	{
		public:

		True () {}

		Type type () const
		{
			return Object::TRUE;
		}
	};

	class False : public Object
	{
		public:

		False () {}

		Type type () const
		{
			return Object::FALSE;
		}
	};

	class Class
	{
		public:

		Class (const std::string& name);
		Class (const std::string& name, std::shared_ptr<Class> base);

		const std::string& name () const;
		std::shared_ptr<Class> base () const;
		std::shared_ptr<Scope> scope () const;

		void add_func (std::shared_ptr<Function> func);

		std::shared_ptr<Function> find_func (const std::string& name) const;

		private:

		const std::string m_name;
		std::shared_ptr<Class> m_base;
		std::shared_ptr<Scope> m_scope;

		std::vector<std::shared_ptr<Function>> m_funcs;
	};

	class Function
	{
		public:

		Function (const std::string& name);
		Function (const std::string& name, const std::vector<std::string>& args);

		const std::string& name () const;
		const std::vector<std::string>& args () const;
		std::shared_ptr<CodeBlock> code () const;
		std::shared_ptr<Scope> scope () const;

		void set_scope (std::shared_ptr<Scope> scope);

		private:

		const std::string m_name;
		const std::vector<std::string> m_args;

		// Function defines its arguments inside of the scope upon construction.
		std::shared_ptr<Scope>	   m_scope;
		std::shared_ptr<CodeBlock> m_code;
	};
}